package aipserver

import (
	"apedge/dao"
	"apedge/dto"
	"apedge/pkg/common"
	"apedge/pkg/database"
	"apedge/pkg/loggers"
	"apedge/pkg/protocol"
	"apedge/pkg/utils"
	"encoding/json"
	"os"
	"strings"
	"time"

	"github.com/jinzhu/gorm"
)

var logger = loggers.LogInstance()

func CreateNode(req dto.CreateNodeReq, userInfo *protocol.UserInfoAAA) (string, error) {
	var err error

	tx := database.Db.Begin()
	defer func() {
		if r := recover(); r != nil {
			tx.Rollback()
		}
	}()
	// 名字是否存在
	exist, err := CheckNodeNameExist(tx, req.NodeName)
	if err != nil {
		tx.Rollback()
		return "", err
	}
	if exist {
		tx.Rollback()
		return "", common.ErrNodeExist
	}
	// 创建node证书
	nodeId := GenNodeUuid()
	err = CreateEdgeNodeCert(userInfo, nodeId)
	if err != nil {
		logger.Errorln("create node cert err: ", err)
		return "", common.ErrCreateNodeCert
	}
	now := time.Now()
	t := &dao.ApedgeNode{
		OrgId:        int64(userInfo.OrgId),
		GroupId:      int64(userInfo.GroupId),
		UserId:       int64(userInfo.UserId),
		UserName:     userInfo.UserName,
		NodeName:     req.NodeName,
		NodeId:       nodeId,
		Describe:     req.Describe,
		NodeType:     req.NodeType,
		DeviceSeries: req.DeviceSeries,
		Status:       common.NODE_STATUS_UNINSTALL,
		CreatedAt:    &now,
		UpdatedAt:    &now,
	}

	if err = t.Save(tx); err != nil {
		tx.Rollback()
		return "", err
	}
	return t.NodeId, tx.Commit().Error
}

func GetNodeList(req dto.GetNodeListReq, userInfo *protocol.UserInfoAAA) ([]dto.OutApedgeNodeItem, int64, error) {
	var err error
	var items []dao.ApedgeNode
	var count int64
	var t dao.ApedgeNode

	tx := database.Db
	ds := []string{}
	if req.DeviceSeries != "" {
		ds = strings.Split(req.DeviceSeries, ",")
	}

	items, count, err = t.GetNodeList(tx, req.PageNum, req.PageSize, req.NodeName, req.Sort, req.Status, ds, userInfo)
	if err != nil {
		return nil, 0, err
	}
	outPutList := []dto.OutApedgeNodeItem{}
	for _, item := range items {
		outItem, err := GenOutApedgeNodeItem(item)
		if err != nil {
			return nil, 0, err
		}
		outPutList = append(outPutList, outItem)
	}
	return outPutList, count, err
}

func GetNodeDetail(nodeId string) (*dto.OutApedgeNodeItem, error) {
	var err error
	var outItem dto.OutApedgeNodeItem

	tx := database.Db
	t := &dao.ApedgeNode{
		NodeId: nodeId,
	}
	nodeItem, err := t.GetNodeByNodeId(tx)
	if err != nil {
		return nil, err
	}
	outNodeSvcList, err := GenOutNodeSvcList(tx, nodeId)
	if err != nil {
		return nil, err
	}
	outItem, err = GenOutApedgeNodeItem(*nodeItem)
	outItem.ServiceList = outNodeSvcList
	outItem.ServiceNum = int64(len(outNodeSvcList))
	if err != nil {
		return nil, err
	}
	return &outItem, nil
}

func DelNode(nodeId string) error {

	tx := database.Db.Begin()
	defer func() {
		if r := recover(); r != nil {
			tx.Rollback()
		}
	}()

	t := dao.ApedgeNode{
		NodeId: nodeId,
	}
	item, err := t.GetNodeByNodeId(tx)
	if err != nil {
		if err.Error() == common.DB_ERR_RECORD_NOT_FOUND {
			return common.ErrNodeNotExist
		}
		return err
	}
	if item.Status == common.NODE_STATUS_ONLINE {
		return common.ErrStatusDelete
	}
	err = t.DeleteByNodeId(tx)
	if err != nil {
		return err
	}
	return tx.Commit().Error
}

func CreateNodeCert(nodeId string, userInfo *protocol.UserInfoAAA) error {
	tx := database.Db.Begin()
	defer func() {
		if r := recover(); r != nil {
			tx.Rollback()
		}
	}()

	exist, err := CheckNodeIdExist(tx, nodeId)
	if err != nil {
		return err
	}
	if !exist {
		return common.ErrNodeNotExist
	}

	err = CreateEdgeNodeCert(userInfo, nodeId)
	if err != nil {
		logger.Errorln("create node cert err: ", err)
		return common.ErrCreateNodeCert
	}
	return nil
}

func GetInstallScripts(req dto.GetInstallScriptReq, userInfo *protocol.UserInfoAAA) ([]string, error) {

	tx := database.Db.Begin()
	defer func() {
		if r := recover(); r != nil {
			tx.Rollback()
		}
	}()

	// 检查Node是否存在
	_, exsit, err := CheckNodeIsExistByNodeID(tx, req.NodeId)
	if err != nil {
		tx.Rollback()
		return nil, err
	}
	if !exsit {
		tx.Rollback()
		return nil, common.ErrNodeNotExist
	}

	scripts, err := CreateInstallScripts(userInfo, req.NodeId)
	if err != nil {
		logger.Errorln("CreateInstallScripts err: ", err)
		return nil, common.ErrCreateInstallScripts
	}

	return scripts, tx.Commit().Error
}

func GetNodeCertificate(req dto.GetNodeCertificateReq, userInfo *protocol.UserInfoAAA) (*os.File, string, error) {
	packagePath := GetPackagePath(req.NodeId)
	file, err := os.Open(packagePath)
	if err != nil {
		return nil, packagePath, err
	}
	return file, packagePath, nil
}

func GenOutNodeSvcList(tx *gorm.DB, nodeId string) ([]dto.OutNodeServiceItem, error) {
	var outNodeSvcList []dto.OutNodeServiceItem

	sn := dao.ApedgeServiceNode{
		NodeId: nodeId,
	}
	snItems, err := sn.GetListByNodeId(tx)
	if err != nil {
		return nil, err
	}
	for _, item := range snItems {
		s := dao.ApedgeInference{
			ServiceId: item.ServiceId,
		}
		svc, err := s.GetByServiceId(tx)
		if err != nil {
			return nil, err
		}
		apModels := []dao.ApflowModel{}
		err = json.Unmarshal([]byte(svc.Models), &apModels)
		if err != nil {
			return nil, err
		}
		outns := dto.OutNodeServiceItem{
			ServiceId:     svc.ServiceId,
			ServiceName:   svc.Name,
			NodeId:        item.NodeId,
			ServiceNodeId: item.ServiceNodeId,
			Status:        item.Status,
			Models:        apModels,
			CreatedAt:     utils.TimeToTimestamp(item.CreatedAt),
		}
		outNodeSvcList = append(outNodeSvcList, outns)
	}
	return outNodeSvcList, nil
}
